

#include <stdio.h>
#define _CRT_RAND_S
#include <stdlib.h>
#include <string.h>
#include <gl/glut.h>
#include "utils.h"
#include <vector>
#include <iostream>
static const float g_ship_offset = 0.1f;
namespace Sea_Battle
{
	void DrawQube(float x, float y, float z, float x_size, float y_size, float z_size)
	{
		glBegin(GL_QUADS);
			glVertex3f(x, y+y_size, z);
			glVertex3f(x+x_size, y+y_size, z);
			glVertex3f(x+x_size, y, z);
			glVertex3f(x, y, z);

			//front
			glVertex3f(x, y+y_size, z + z_size);
			glVertex3f(x+x_size, y+y_size, z + z_size);
			glVertex3f(x+x_size, y, z + z_size);
			glVertex3f(x, y, z + z_size);
			
			//up
			glVertex3f(x, y + y_size, z);
			glVertex3f(x + x_size, y + y_size, z);
			glVertex3f(x + x_size, y + y_size, z + z_size);
			glVertex3f(x, y + y_size, z + z_size);
		
			//down
			glVertex3f(x, y, z);
			glVertex3f(x + x_size, y, z);
			glVertex3f(x + x_size, y, z + z_size);
			glVertex3f(x, y, z + z_size);

			//left
			glVertex3f(x, y, z);
			glVertex3f(x, y + y_size, z);
			glVertex3f(x, y + y_size, z + z_size);
			glVertex3f(x, y, z + z_size);

			//right
			glVertex3f(x + x_size, y, z);
			glVertex3f(x + x_size, y + y_size, z);
			glVertex3f(x + x_size, y + y_size, z + z_size);
			glVertex3f(x + x_size, y, z + z_size);
		glEnd();
	}

	void DrawShip(int x, int y)
	{
		DrawQube(x+g_ship_offset, y+g_ship_offset, 1.01f, 1.0f-g_ship_offset*2, 1.0f-g_ship_offset*2, 0.2f);
	}

	using namespace Graph_Utils;
	class BattleShip
	{
		//Ship* m_ships;
		bool m_is_alive;
		int m_x, m_y;
		int m_size;
		bool m_verticale;
		bool m_is_ready;

	public:
		bool* m_map;

		BattleShip()
			: m_x(-1)
			, m_y(-1)
		{
		}

		void Save(FILE* f)
		{
			fwrite(&m_is_alive, sizeof(bool), 1, f);
			fwrite(&m_x, sizeof(int), 1, f);
			fwrite(&m_y, sizeof(int), 1, f);
			fwrite(&m_size, sizeof(int), 1, f);
			fwrite(&m_verticale, sizeof(bool), 1, f);
			//fwrite(&m_is_ready, sizeof(bool), 1, f);
			fwrite(m_map, sizeof(bool), m_size, f);
		}

		void Load(FILE* f)
		{
			delete[] m_map;
			m_map = nullptr;
			fread(&m_is_alive, sizeof(bool), 1, f);
			fread(&m_x, sizeof(int), 1, f);
			fread(&m_y, sizeof(int), 1, f);
			fread(&m_size, sizeof(int), 1, f);
			SetSize(m_size);
			fread(&m_verticale, sizeof(bool), 1, f);
			m_is_ready = true;
			fread(m_map, sizeof(bool), m_size, f);
		}

		bool IsKilled()
		{
			for(int i=0; i<m_size; ++i)
			{
				if(!m_map[i]) return false;
			}

			return true;
		}

		bool IsShutted()
		{
			for(int i=0; i<m_size; ++i)
			{
				if(m_map[i]) return true;
			}

			return false;
		}

		BattleShip(int x, int y, int size, bool verticale)
			: m_size(size)
			, m_x(x)
			, m_y(y)
			, m_is_alive(true)
			, m_verticale(true)
			, m_map(nullptr)
			, m_is_ready(false)
		{
			SetSize(size);
		}

		~BattleShip()
		{
			delete[] m_map;
			m_map = nullptr;
			m_size = 0;
		}

		void SetReady(bool ready = true)
		{
			if(!(m_is_ready = ready))
			{
				m_x = -1;
				m_y = -1;
			}
		}

		bool IsReady() const
		{
			return m_is_ready;
		}

		void SetVert(bool verticale)
		{
			m_verticale = verticale;
		}

		void SetSize(int size)
		{
			delete[] m_map;
			m_map = new bool[size];
			memset(m_map, 0, sizeof(bool) * size);
			m_size = size;
		}

		int GetSize() const
		{
			return m_size;
		}

		int GetX() const
		{
			return m_x;
		}

		int GetY() const
		{
			return m_y;
		}

		bool IsVert() const
		{
			return m_verticale;
		}

		void Attack(int p)
		{
			m_map[p] = false;
		}

		bool IsMyCoord(int x, int y)
		{
			if(m_verticale)
			{
				return m_x == x && y >= m_y && y < m_y + m_size;
			}

			return m_y == y && x >= m_x && x < m_x + m_size;
		}

		bool IsAlive(int p)
		{
			return m_map[p];
		}

		void Move(int x, int y)
		{
			m_x = x;
			m_y = y;
		}

		bool Attack(int x, int y)
		{
			int p = m_verticale ? y - m_y : x - m_x;
			if(m_map[p]) return false;
			m_map[p] = true;
			return true;
		}

		void Draw(bool fully_draw)
		{
			if(m_is_ready)
			{
				float color_map[3][3] =
				{
					{0.8f, 0.8f, 0.0f},
					{0.8f, 0.0f, 0.0f},
					{0.0f, 0.8f, 0.0f},
				};

				int color_p;
				if(IsKilled())
				{
					color_p = 1;
				}
				else if(IsShutted())
				{
					color_p = 2;
				}
				else if(fully_draw)
				{
					color_p = 0;
				}
				else
					return;

				int xo = m_verticale ? 0 : 1;
				int yo = m_verticale ? 1 : 0;
				int x = m_x;
				int y = m_y;

				for(int i=0; i<m_size; ++i, x += xo, y += yo)
				{
					if(m_map[i])
					{
						glColor3fv(color_map[color_p]);
					}
					else if(fully_draw)
					{
						glColor3fv(color_map[0]);
					}
					else
						continue;

					DrawShip(x, y);
				}
			}
		}
	};

	int ShipCounts[4] = {4, 3, 2, 1};
	int ShipSizes[4] = {1, 2, 3, 4};

	struct pos
	{
		int x, y;
	};

	class Player
	{
		static const int m_ship_count = 4 * 3 * 2 * 1;
		BattleShip m_ships[m_ship_count];

	public:
		std::vector<pos> last_attack;
		std::vector<pos> m_free_movs;
		std::vector<pos> m_attacked;
	
		Player ()
		{
			Reset();
		}

		static const int X_coordinat = 10;
		static const int Y_coordinat = 10;
		bool lose_flag;
		bool is_build_mode;
		int cur_ship_type;
		int cur_ship;
		int cur_ship_p;

		Player* m_opponent;

	public:
		void SetOpponent(Player* opponent)
		{
			m_opponent = opponent;
		}

		void Reset()
		{
			last_attack.clear();
			cur_ship_type = 3;
			cur_ship = -1;
			cur_ship_p = -1;

			is_build_mode = true;
			lose_flag = false;

			for(int i=0; i<m_ship_count; ++i)
			{
				m_ships[i].SetReady(false);
			}

			m_attacked.clear();
			m_free_movs.clear();
			for (int i=0;i<X_coordinat;i++)
			{
				for (int j=0; j<Y_coordinat;j++)
				{
					pos p;
					p.x = i;
					p.y = j;
					m_free_movs.push_back(p);
				}
			}
		}

		void RemoveFreeMov(int x, int y)
		{
			for(size_t i=0; i<m_free_movs.size(); ++i)
			{
				if(m_free_movs[i].y == y && m_free_movs[i].x == x)
				{
					m_free_movs.erase(m_free_movs.begin() + i);
					return;
				}
			}
		}

		bool UI()
		{
			if(is_build_mode)
			{
				while(NextShip())
				{
					m_ships[cur_ship_p].SetVert(rand() % 2 ? true : false);

					int x = rand() % X_coordinat;
					int y = rand() % Y_coordinat;
					m_ships[cur_ship_p].Move(x, y);

					if(bool bad = IsCurBadPos())
					{
						pos st[] = {{-1, -1}, {1, -1}, {-1, 1}, {1, 1}};
						for(int i=0; bad && i<sizeof(st) / sizeof(pos); ++i)
						{
							while(bad)
							{
								m_ships[cur_ship_p].Move(m_ships[cur_ship_p].GetX()+st[i].x, y);
								if(m_ships[cur_ship_p].GetX() < 0 || m_ships[cur_ship_p].GetX() >= X_coordinat) break;
								bad = IsCurBadPos();

								while(bad)
								{
									m_ships[cur_ship_p].Move(m_ships[cur_ship_p].GetX(), m_ships[cur_ship_p].GetY()+st[i].y);
									if(m_ships[cur_ship_p].GetY() < 0 || m_ships[cur_ship_p].GetX() >= Y_coordinat) break;
									bad = IsCurBadPos();
								}
							}
						}

						if(bad)
						{
							std::cout << "UI error: position not found." << std::endl;
							lose_flag = true;
							return false;
						}
					}
				}

				is_build_mode = false;
				return false;
			}

			if(!m_opponent)
			{
				std::cout << "UI error: opponent is not defined." << std::endl;
				lose_flag = true;
				return false;
			}

			if(!m_free_movs.size())
			{
				std::cout << "UI error: movs not found." << std::endl;
				lose_flag = true;
				return false;
			}

			int s = -1;
			if(last_attack.size())
			{
				if(last_attack.size() >= 2)
				{
					int way[] = {-1, 1};
					
					if(last_attack[0].x - last_attack[1].x)
					{
						for(int i=0, w=rand()%2;s==-1 && i<2; ++i, w=++w % 2)
						{
							for(size_t i0=0; s==-1 && i0<last_attack.size(); i0++)
							{
								for(size_t j=0; j<m_free_movs.size(); ++j)
								{
									if(m_free_movs[j].x == last_attack[i0].x+way[w]
									&& m_free_movs[j].y == last_attack[i0].y)
									{
										s = j;
										break;
									}
								}
							}
						}
					}
					else
					{
						for(int i=0, w=rand()%2;s==-1 && i<2; ++i, w=++w % 2)
						{
							for(size_t i0=0; s==-1 && i0<last_attack.size(); i0++)
							{
								for(size_t j=0; j<m_free_movs.size(); ++j)
								{
									if(m_free_movs[j].x == last_attack[i0].x
									&& m_free_movs[j].y == last_attack[i0].y+way[w])
									{
										s = j;
										break;
									}
								}
							}
						}
					}
				}
				else
				{
					pos way[] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};

					for(int w=rand()%4, i=0; s ==-1&& i<4; w = ++w%4, i++)
					{
						for(size_t j=0; j<m_free_movs.size(); ++j)
						{
							if(m_free_movs[j].y == last_attack[0].y+way[w].y
								&& m_free_movs[j].x == last_attack[0].x+way[w].x)
							{
								s = j;
								break;
							}
						}
					}
				}
			}
			
			if(s == -1) s = rand() % m_free_movs.size();

			pos p = m_free_movs[s];
			m_free_movs.erase(m_free_movs.begin() + s);
			switch(Attack(p.x, p.y))
			{
			case 0:
				std::cout << "UI error: bad attack." << std::endl;
				lose_flag = true;
			return false;

			case -1:
			{
				int i = last_attack.size();
				last_attack.push_back(p);
				RemoveFreeMov(last_attack[i].x+1, last_attack[i].y+1);
				RemoveFreeMov(last_attack[i].x-1, last_attack[i].y-1);
				RemoveFreeMov(last_attack[i].x-1, last_attack[i].y+1);
				RemoveFreeMov(last_attack[i].x+1, last_attack[i].y-1);
			}
			return true;

			case -2:
				for(size_t i=0; i<last_attack.size(); ++i)
				{
					RemoveFreeMov(last_attack[i].x, last_attack[i].y+1);
					RemoveFreeMov(last_attack[i].x, last_attack[i].y-1);
					RemoveFreeMov(last_attack[i].x+1, last_attack[i].y);
					RemoveFreeMov(last_attack[i].x-1, last_attack[i].y);
				}

				last_attack.clear();
			return true;
			}

			return false;
		}

		void Save(FILE* f)
		{
			for(int i=0; i<m_ship_count; ++i)
			{
				m_ships[i].Save(f);
			}
		}

		void Load(FILE* f)
		{
			for(int i=0; i<m_ship_count; ++i)
			{
				m_ships[i].Load(f);
			}
		}

		void CheckLose()
		{
			for(int i=0; i<m_ship_count; ++i)
			{
				if(!m_ships[i].IsKilled()) return;
			}

			lose_flag = true;
		}

		int Attack(int x, int y)
		{
			for(int i=0; i<m_ship_count; ++i)
			{
				if(m_opponent->m_ships[i].IsReady() && m_opponent->m_ships[i].IsMyCoord(x, y))
				{
					if(!m_opponent->m_ships[i].Attack(x, y)) return 0;

					if(m_opponent->m_ships[i].IsKilled())
					{
						m_opponent->CheckLose();
						return -2;
					}

					return -1;
				}
			}

			for(size_t i=0; i<m_opponent->m_attacked.size(); ++i)
			{
				if(m_opponent->m_attacked[i].x == x && m_opponent->m_attacked[i].y == y)
					return 0;
			}

			pos p;
			p.x = x;
			p.y = y;
			m_opponent->m_attacked.push_back(p);
			return 1;
		}

		bool NextShip()
		{
			if(++cur_ship >= ShipCounts[cur_ship_type])
			{
				cur_ship = 0;
				if(--cur_ship_type < 0) return false;
			}

			if(++cur_ship_p >= m_ship_count) return false;

			m_ships[cur_ship_p].SetSize(ShipSizes[cur_ship_type]);
			m_ships[cur_ship_p].SetReady(true);

			return true;
		}

		bool IsBadPos(int size, bool is_vert, int x, int y, BattleShip* s=nullptr)
		{
			if(is_vert)
			{
				if(x < 0 || x >= 10 || y < 0 || y + size > 10) return true;
			}
			else
			{
				if(x < 0 || x + size > 10 || y < 0 || y >= 10) return true;
			}

			for(int i=0; i<size; ++i)
			{
				const int xo = is_vert ? 0 : i;
				const int yo = is_vert ? i : 0;

				if(IsUsed(s, x + xo, y + yo)) return true;
				if(x - 1 >= 0 && y - 1 >= 0 && IsUsed(s, x - 1 + xo, y - 1 + yo)) return true;
				if(x + 1 < 10 && y + 1 < 10 && IsUsed(s, x + 1 + xo, y + 1 + yo)) return true;
				if(x - 1 >= 0 && y + 1 < 10 && IsUsed(s, x - 1 + xo, y + 1 + yo)) return true;
				if(x + 1 < 10 && y - 1 >= 0 && IsUsed(s, x + 1 + xo, y - 1 + yo)) return true;

				if(x + 1 < 10 && IsUsed(s, x + 1 + xo, y + yo)) return true;
				if(x - 1 >= 0 && IsUsed(s, x - 1 + xo, y + yo)) return true;
				if(y + 1 < 10 && IsUsed(s, x + xo, y + 1 + yo)) return true;
				if(y - 1 >= 0 && IsUsed(s, x + xo, y - 1 + yo)) return true;
			}

			return false;
		}

		bool IsCurBadPos(int x, int y)
		{
			if(!GetCurShip()) return true;

			return IsBadPos(
				m_ships[cur_ship_p].GetSize(),
				m_ships[cur_ship_p].IsVert(),
				x, y,
				&m_ships[cur_ship_p]);
		}

		bool IsCurBadPos()
		{
			return IsCurBadPos(m_ships[cur_ship_p].GetX(), m_ships[cur_ship_p].GetY());
		}

		BattleShip* GetCurShip()
		{
			if(cur_ship_p >= m_ship_count) return nullptr;

			return &m_ships[cur_ship_p];
		}

		bool IsUsed(BattleShip* ship, int x, int y)
		{
			for(int i=0; i<m_ship_count; ++i)
			{
				if(ship == &m_ships[i]) continue;
				if(m_ships[i].IsReady() && m_ships[i].IsMyCoord(x, y)) return true;
			}

			return false;
		}

		void Draw(bool fully_draw)
		{
			glColor3f(0.0f, 0.0f, 0.8f);
			const float offset = 0.1f;

			for(size_t i=0; i<m_attacked.size(); ++i)
			{
				DrawShip(m_attacked[i].x, m_attacked[i].y);
			}

			for(int i=0; i<m_ship_count; ++i)
			{
				m_ships[i].Draw(fully_draw);
			}
		}
	};
}
